//------------------------------------------------------------
//  Filename: eth_mac_filter.sv
//   
//  Author  : wlduan@gmail.com
//  Revise  : 2020-12-02 11:48
//  Description: 
//   
//  Copyright (C) 2014, YRBD, Inc. 					      
//  All Rights Reserved.                                       
//-------------------------------------------------------------
//
`timescale 1ns/1ps
 
module eth_mac_filter  #( 
    parameter DEEPTH = 8,  // mus bigger than 6 
    parameter PTR_WIDTH = $clog2(DEEPTH)) 
( 
    input  logic            clk_i, 
    input  logic            rstn_i, 

    input  logic            mac_rx_strip , // without fcs
    input  logic            mac_cfg_loop , // without fcs
    input  logic            mac_lp_ready , // without fcs

    input  logic[7:0]       mac_rx_data  ,   
    input  logic            mac_rx_valid , 
    input  logic            mac_rx_sof   ,   
    input  logic            mac_rx_eof   ,   
    input  logic            mac_rx_err   ,  

    input  logic            mac_ft_abort ,
    output logic[7:0]       mac_ft_data  ,   
    output logic            mac_ft_valid , 
    input  logic            mac_ft_ready , 
    output logic            mac_ft_sof   ,   
    output logic            mac_ft_eof   ,   
    output logic            mac_ft_err     
);
//----------------------------------------------------------
logic                 rstn;
logic                 clear; // abort ? clear all buffered data
logic                 ferr;
logic [7 : 0]         wdata; // 
logic                 wvalid; 
logic                 wready;
logic [7 : 0]         rdata; // 
logic                 rvalid; 
logic                 rready;
//----------------------------------------------------------
logic[PTR_WIDTH-1: 0] buf_wr; 
logic[PTR_WIDTH-1: 0] buf_rd; 
logic[PTR_WIDTH-1: 0] buf_wr_q; 
logic[PTR_WIDTH-1: 0] buf_rd_q; 
logic[PTR_WIDTH  : 0] buf_cnt; 
logic[PTR_WIDTH  : 0] buf_cnt_q;
//----------------------------------------------------------
assign rstn = (~clear)&rstn_i;
//----------------------------------------------------------
always_ff@(posedge clk_i,negedge rstn)begin 
    if(!rstn)begin 
        buf_wr_q <= 'h0; 
    end
    else if(wvalid&wready) begin 
        buf_wr_q <= buf_wr; 
    end 
end 
//----------------------------------------------------------
assign buf_wr = (wvalid&wready)?(buf_wr_q + 1): buf_wr_q; 
//----------------------------------------------------------
always_ff@(posedge clk_i,negedge rstn)begin 
    if(!rstn)begin 
        buf_rd_q <= 'h0; 
    end
    else if(rvalid&rready) begin 
        buf_rd_q <= buf_rd; 
    end 
end 
//----------------------------------------------------------
assign buf_rd = (rvalid&rready)?(buf_rd_q + 1):buf_rd_q; 
//----------------------------------------------------------
assign wready = (buf_cnt_q < DEEPTH)? 1'b1 : 1'b0; 
//----------------------------------------------------------
enum logic[4:0] {IDLE,HEAD,DATA,STRIP,KEEP,DROP} cs,ns;
//--------------------------------------------------------
always_ff @ (posedge clk_i,negedge rstn) begin
    if(~rstn) begin
        cs <= IDLE;
    end
    else begin
        cs <= ns;
    end
end
//--------------------------------------------------------
always_comb begin
    ns = cs;
    case(cs)
        IDLE: begin
            if(mac_rx_sof) ns = HEAD;
        end
        HEAD: begin
            if(buf_cnt_q > 6) ns = DATA;
        end
        DATA: begin
            if(mac_rx_eof) ns = (mac_rx_strip)?STRIP:KEEP;
            else if(buf_cnt_q == DEEPTH) ns = DROP;
        end
        STRIP: begin
            ns = KEEP;
        end
        KEEP: begin
           if(mac_ft_eof) ns = DROP;
        end
        DROP: begin
            ns = IDLE;
        end 
    endcase
end
//----------------------------------------------------------
always_comb begin
    rvalid = 1'b0;
    if (cs == DATA) begin
        rvalid = (buf_cnt_q > 4);
    end
    else if(cs == KEEP)begin
        rvalid = (buf_cnt_q > 0);
    end
end
//----------------------------------------------------------
always_comb begin 
    buf_cnt = buf_cnt_q; 
    if(wvalid&wready&rvalid&rready)begin 
        buf_cnt = buf_cnt_q; 
    end 
    else if(rvalid&rready) begin 
        buf_cnt = buf_cnt_q - 1'b1;
    end 
    else if(wvalid&wready) begin 
        buf_cnt = buf_cnt_q + 1'b1; 
    end 
    else if(cs == STRIP) begin
        buf_cnt = buf_cnt_q - 3'b100; 
    end
end 
//----------------------------------------------------------
always_ff@(posedge clk_i,negedge rstn)begin 
    if(!rstn)begin 
        buf_cnt_q <= 'h0; 
    end 
    else begin 
        buf_cnt_q <= buf_cnt; 
    end 
end 
//----------------------------------------------------------
logic[7:0] buf_bits[DEEPTH-1:0]; 
//----------------------------------------------------------
genvar i;
generate 
    for (i = 0;i < DEEPTH; i ++) begin
        always_ff@(posedge clk_i,negedge rstn)begin
            if(!rstn)begin 
                buf_bits[i] <= '0; 
            end 
            else if(wvalid&wready&(buf_wr_q == i)) begin
                buf_bits[i] <= wdata; 
            end 
        end 
    end
endgenerate
//----------------------------------------------------------
assign clear  = (mac_ft_abort||(cs == DROP));
//----------------------------------------------------------
assign rdata  = (rvalid&rready)? buf_bits[buf_rd_q]:'b0; 
//----------------------------------------------------------
assign wdata  = mac_rx_data;
assign wvalid = mac_rx_valid;
//----------------------------------------------------------
assign rready = mac_cfg_loop ? mac_lp_ready : mac_ft_ready;
assign mac_ft_data  = rdata;
assign mac_ft_valid = rvalid;
//----------------------------------------------------------
always_ff@(posedge clk_i,negedge rstn)begin 
    if(!rstn)begin 
        ferr <= 1'h0; 
    end 
    else if(cs == HEAD)begin 
        ferr <= 1'b1; 
    end 
end
//----------------------------------------------------------
always_ff@(posedge clk_i,negedge rstn)begin 
    if(!rstn)begin 
        mac_ft_sof <= 1'h0; 
    end 
    else if((cs == HEAD)&&(buf_cnt_q > 6))begin 
        mac_ft_sof <= 1'b1; 
    end 
    else if(rvalid&rready) begin
        mac_ft_sof <= 1'h0; 
    end
end
//----------------------------------------------------------
assign mac_ft_eof = (cs == KEEP)&(buf_cnt_q == 1);
assign mac_ft_err = ferr;
//----------------------------------------------------------

endmodule
